home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (c) 1992 The Geometry Center; University of Minnesota
- 1300 South Second Street; Minneapolis, MN 55454, USA;
-
- This file is part of geomview/OOGL. geomview/OOGL is free software;
- you can redistribute it and/or modify it only under the terms given in
- the file COPYING, which you should have received along with this file.
- This and other related software may be obtained via anonymous ftp from
- geom.umn.edu; email: software@geom.umn.edu. */
- static char *copyright = "Copyright (C) 1992 The Geometry Center";
-
- /* Authors: Charlie Gunn, Stuart Levy, Tamara Munzner, Mark Phillips */
-
- #include <math.h>
- #include "geomclass.h"
- #include "cameraP.h"
- #include "mg.h"
-
- Camera * _CamSet(Camera *cam, int attr, va_list *a_list);
-
- #define SETFLAG(flag, bit, value) \
- if (value) flag |= bit; \
- else flag &= ~bit
-
- #define GETFLAG(flag, bit) ( (flag & bit) != 0 )
-
- static float GetHalfField( register Camera *cam );
- static void SetHalfField( register Camera *cam, float halffield );
-
- Camera *
- CamCreate(int a1, ...)
- {
- register Camera *thiscam;
- va_list a_list;
-
- thiscam = OOGLNewE(Camera, "CamCreate: unable to allocate camera\n");
- if (thiscam == NULL) return(NULL);
-
- RefInit((Ref *)thiscam, CAMMAGIC);
-
- CamDefault(thiscam);
- thiscam->changed = 0;
-
- va_start( a_list, a1 );
- _CamSet(thiscam, a1, &a_list);
- va_end(a_list);
- return thiscam;
- }
-
- void
- CamDefault(Camera *cam)
- {
- cam->flag = CAMF_PERSP;
- cam->frameaspect = 4.0/3.0;
- cam->focus = 3.0;
- cam->stereo_sep = 0.5;
- cam->stereo_angle = .08;
- cam->c2whandle = NULL;
- cam->w2chandle = NULL;
- cam->sterhandle[0] = NULL;
- cam->sterhandle[1] = NULL;
- CamStereoCompute(cam);
- cam->whicheye = 0; /* only applies to stereo */
- cam->space = TM_EUCLIDEAN;
- CamReset( cam );
- }
-
- Camera *
- CamSet(Camera *cam, int a1, ...)
- {
- va_list a_list;
-
- va_start(a_list, a1);
- return ( _CamSet(cam, a1, &a_list) );
- }
-
- Camera *
- _CamSet(Camera *cam, int attr, register va_list *alist)
- {
- Transform * tt;
- int sethalffield = 0, setaspect = 0, setstereogeom = 0;
- double halffield, v;
- Handle *h;
- int bit, unbit;
- char **ablock = NULL;
-
- #define NEXT(type) OOGL_VA_ARG(type,alist,ablock)
-
- while (attr != CAM_END) {
- bit = unbit = 0;
- switch(attr) {
- case CAM_ABLOCK:
- ablock = NEXT(char**);
- break;
- case CAM_C2W:
- tt = NEXT(Transform *);
- bit = CAMF_NEWC2W, unbit = CAMF_W2C;
- TmCopy(tt, cam->camtoworld);
- TmInvert( cam->camtoworld, cam->worldtocam);
- break;
- case CAM_W2C:
- tt = NEXT(Transform *);
- bit = CAMF_W2C, unbit = CAMF_NEWC2W;
- TmCopy(tt, cam->worldtocam);
- TmInvert(cam->worldtocam, cam->camtoworld);
- break;
- case CAM_FOV:
- v = NEXT(double) / 2;
- bit = CAMF_FOV;
- if(cam->flag & CAMF_PERSP) {
- if(v >= 180/2) v = 120/2;
- v = tan( RADIANS(v) );
- }
- halffield = v;
- sethalffield = 1;
- break;
- case CAM_HALFYFIELD:
- cam->halfyfield = NEXT(double);
- if(cam->flag & CAMF_PERSP)
- cam->halfyfield *= cam->focus;
- bit = CAMF_FOV;
- break;
- case CAM_HALFFIELD:
- halffield = NEXT(double);
- sethalffield = 1;
- bit = CAMF_FOV;
- break;
- case CAM_ASPECT:
- if((v = NEXT(double)) > 0.) {
- if (!sethalffield)
- halffield = GetHalfField(cam);
- cam->frameaspect = v;
- bit = CAMF_ASPECT;
- setaspect = 1;
- }
- break;
- case CAM_FOCUS:
- if((v = NEXT(double)) > 0) {
- if(cam->flag & CAMF_PERSP)
- cam->halfyfield *= v / cam->focus;
- cam->focus = v;
- bit = CAMF_FOCUS;
- }
- break;
- case CAM_NEAR:
- cam->near = NEXT(double);
- bit = CAMF_NEAR;
- break;
- case CAM_FAR:
- cam->far = NEXT(double);
- bit = CAMF_FAR;
- break;
- case CAM_STEREOSEP:
- cam->stereo_sep = NEXT(double);
- bit = CAMF_STEREOGEOM, unbit = CAMF_STEREOXFORM;
- setstereogeom = 1;
- break;
- case CAM_STEREOANGLE:
- cam->stereo_angle = NEXT(double);
- bit = CAMF_STEREOGEOM, unbit = CAMF_STEREOXFORM;
- setstereogeom = 1;
- break;
- case CAM_STEREOEYE:
- cam->whicheye = NEXT(int);
- bit = CAMF_EYE;
- break;
- case CAM_PERSPECTIVE: bit = CAMF_PERSP; goto flagbit;
- case CAM_STEREO: bit = CAMF_STEREO; goto flagbit;
- flagbit:
- SETFLAG(cam->flag, bit, NEXT(int));
- break;
- case CAM_STEREYES:
- bcopy(NEXT(float *), cam->stereyes, 2*sizeof(Transform));
- bit = CAMF_STEREOXFORM, unbit = CAMF_STEREOGEOM;
- break;
- case CAM_STERHANDLES:
- bcopy(NEXT(Handle **), cam->sterhandle, 2*sizeof(Handle *));
- bit = CAMF_STEREOXFORM, unbit = CAMF_STEREOGEOM;
- break;
- case CAM_C2WHANDLE:
- h = NEXT(Handle *);
- if(cam->c2whandle && cam->c2whandle != h)
- HandlePDelete(&cam->c2whandle);
- cam->c2whandle = h;
- HandleRegister(&cam->c2whandle, (Ref *)cam, cam->camtoworld, CamTransUpdate);
- bit = CAMF_NEWC2W, unbit = CAMF_W2C;
- break;
-
- case CAM_W2CHANDLE:
- h = NEXT(Handle *);
- HandlePDelete(&cam->w2chandle);
- cam->w2chandle = h;
- HandleRegister(&cam->w2chandle, (Ref *)cam, cam->worldtocam, CamTransUpdate);
- bit = CAMF_W2C, unbit = CAMF_NEWC2W;
- break;
- case CAM_SPACE:
- {
- int space = NEXT(int);
- if ( space != TM_EUCLIDEAN
- && space != TM_HYPERBOLIC
- && space != TM_SPHERICAL) {
- OOGLError(0,"illegal space value %1d\n", space);
- } else {
- cam->space = space;
- bit = CAMF_SPACE;
- }
- }
- break;
- default:
- OOGLError (0, "CamSet: Undefined attribute: %d", attr);
- return NULL;
- }
- cam->changed &= ~unbit;
- cam->changed |= bit;
- attr = NEXT(int);
- }
-
- /*
- (sethalffield) means we have a new halffield value, stored in local
- var "halffield". (setaspect) means we have a new aspect ratio, and the
- halffield must be updated in accordance with this. In this case,
- "halffield" holds either the original halffield value, if a new one
- hasn't been explicitly set with CAM_HALFFIELD or CAM_FOV, or the
- new value, if it was explicitly set. All of these cases are dealt with
- by the following call to SetHalfField.
- */
- if (setaspect || sethalffield)
- SetHalfField(cam, halffield);
-
- /* following works since the only way to change stereo parameters is
- by using this routine */
- if (setstereogeom)
- CamStereoCompute(cam);
-
- return cam;
-
- #undef NEXT
-
- }
-
-
- /*-----------------------------------------------------------------------
- * Function: CamGet
- * Description: query a camera
- * Args: *cam: the camera to query
- * attr: the attribute to query
- * value: attr's value is written here
- * Returns: 1: attr is valid and value has been written
- * 0: attr is valid but currently does not
- * have a value
- * -1: invalid attr
- * Author: mbp
- * Date: Thu Aug 8 10:09:10 1991
- * Notes: At present, there are no camera attr's that might
- * not be set, so 0 is never returned. This might change
- * in the future.
- */
- int
- CamGet(register Camera *cam, int attr, void *value)
- {
- #define VALUE(type) ((type*)value)
-
- switch (attr) {
-
- case CAM_PERSPECTIVE:
- *VALUE(int) = GETFLAG(cam->flag, CAMF_PERSP);
- break;
-
- case CAM_STEREO:
- *VALUE(int) = GETFLAG(cam->flag, CAMF_STEREO);
- break;
-
- case CAM_C2W:
- /* camtoworld is always up to date, so just copy */
- TmCopy( cam->camtoworld, VALUE(Transform) );
- break;
-
- case CAM_W2C:
- /* worldtocam is not always up to date, so update if necessary ... */
- if (cam->flag & CAMF_NEWC2W ) {
- TmInvert( cam->camtoworld, cam->worldtocam );
- cam->flag &= ~CAMF_NEWC2W;
- }
- /* ... then copy */
- TmCopy( cam->worldtocam, VALUE(Transform) );
- break;
-
- case CAM_FOV:
- *VALUE(float) = 2 * ( (cam->flag & CAMF_PERSP)
- ? DEGREES( atan( (double)(GetHalfField(cam)) ) )
- : GetHalfField(cam));
- break;
-
- case CAM_HALFYFIELD:
- *VALUE(float) = (cam->flag & CAMF_PERSP) ? cam->halfyfield / cam->focus
- : cam->halfyfield;
- break;
-
- case CAM_HALFFIELD:
- *VALUE(float) = GetHalfField(cam);
- break;
-
- case CAM_ASPECT:
- *VALUE(float) = cam->frameaspect;
- break;
-
- case CAM_FOCUS:
- *VALUE(float) = cam->focus;
- break;
-
- case CAM_NEAR:
- *VALUE(float) = cam->near;
- break;
-
- case CAM_FAR:
- *VALUE(float) = cam->far;
- break;
-
- case CAM_STEREOSEP:
- *VALUE(float) = cam->stereo_sep;
- break;
-
- case CAM_STEREOANGLE:
- *VALUE(float) = cam->stereo_angle;
- break;
-
- case CAM_STEREOEYE:
- *VALUE(int) = cam->whicheye;
- break;
-
- case CAM_C2WHANDLE:
- *VALUE(Handle *) = cam->c2whandle;
- break;
-
- case CAM_W2CHANDLE:
- *VALUE(Handle *) = cam->w2chandle;
- break;
-
- case CAM_STEREYES:
- bcopy(cam->stereyes, value, 2*sizeof(Transform));
- break;
-
- case CAM_STERHANDLES:
- bcopy(cam->sterhandle, value, 2*sizeof(Handle *));
- break;
-
- case CAM_SPACE:
- *VALUE(int) = cam->space;
- break;
-
- default:
- return -1;
- break;
- }
- return 1;
-
- #undef VALUE
- }
-
- void
- CamDelete( register Camera *cam )
- {
- if(cam && RefDecr((Ref *)cam) <= 0) {
- cam->magic = -1; /* Invalidate */
- if(cam->c2whandle) HandlePDelete( &cam->c2whandle );
- if(cam->w2chandle) HandlePDelete( &cam->w2chandle );
- if(cam->sterhandle[0]) HandlePDelete( &cam->sterhandle[0] );
- if(cam->sterhandle[1]) HandlePDelete( &cam->sterhandle[1] );
- OOGLFree(cam);
- }
- }
-
- Camera *
- CamCopy( Camera *src, register Camera *dst )
- {
- if(src == NULL)
- return NULL;
- if(dst == NULL)
- dst = OOGLNewE(Camera, "CamCopy Camera");
- else
- HandleDelete(dst->handle);
- *dst = *src;
- dst->ref_count = 1;
- dst->handle = NULL;
- return dst;
- }
-
- void
- CamReset( register Camera *cam )
- {
- Transform T;
- int persp;
-
- CamGet(cam, CAM_PERSPECTIVE, &persp);
-
- switch (cam->space) {
-
- case TM_EUCLIDEAN:
- CamSet( cam,
- CAM_NEAR, .07,
- CAM_FAR, 100.0,
- CAM_FOCUS, 3.0,
- CAM_FOV, persp ? 40.0 : 2.2,
- CAM_END);
- break;
-
- case TM_HYPERBOLIC:
- CamSet( cam,
- CAM_NEAR, .07,
- CAM_FAR, 100.0,
- CAM_FOCUS, 2.5,
- CAM_FOV, persp ? 40.0 : 2.2,
- CAM_END);
- break;
-
- case TM_SPHERICAL:
- CamSet( cam,
- CAM_NEAR, .05,
- CAM_FAR, -.05,
- CAM_FOCUS, 0.5,
- CAM_FOV, persp ? 90.0 : 2.2,
- CAM_END);
- break;
- }
-
- TmSpaceTranslate( T, 0.0, 0.0, cam->focus, cam->space );
- CamSet(cam, CAM_C2W, T, CAM_END);
- }
-
- /*
- * Return camera's projection transform in proj.
- * See CamView below for the range of the projection.
- */
- void
- CamViewProjection( register Camera *cam, register Transform proj )
- {
- float y;
- float x;
-
- y = cam->halfyfield;
- if(cam->flag & CAMF_PERSP)
- y *= cam->near / cam->focus;
- x = cam->frameaspect * y;
-
- if(cam->flag & CAMF_PERSP) {
- TmPerspective( proj, -x, x, -y, y, cam->near, cam->far );
- } else {
- TmOrthographic( proj, -x, x, -y, y, cam->near, cam->far );
- }
- if (cam->flag & CAMF_STEREO)
- TmConcat( cam->stereyes[cam->whicheye], proj, proj );
- }
-
- /*
- * Computes complete transformation from world -> projected coordinates
- * and leaves it in T.
- * Projected coordinates map the visible world into -1 <= {X,Y,Z} <= 1,
- * with Z = -1 at the near plane and Z = +1 at the far plane.
- */
- void
- CamView( register Camera *cam, Transform T )
- {
- Transform t;
-
- CamViewProjection( cam, t );
- if(cam->flag & CAMF_NEWC2W) {
- TmInvert( cam->camtoworld, cam->worldtocam );
- cam->flag &= ~CAMF_NEWC2W;
- }
- TmConcat( cam->worldtocam, t, T );
- }
-
- void
- CamRotateX( register Camera *cam, float angle )
- {
- CtmRotateX( cam->camtoworld, angle );
- cam->flag |= CAMF_NEWC2W;
- }
-
- void
- CamRotateY( register Camera *cam, float angle )
- {
- CtmRotateY( cam->camtoworld, angle );
- cam->flag |= CAMF_NEWC2W;
- }
-
- void
- CamRotateZ( register Camera *cam, float angle )
- {
- CtmRotateZ( cam->camtoworld, angle );
- cam->flag |= CAMF_NEWC2W;
- }
-
- /* translate the camera, using the camera's notion of what space it
- is in */
- void
- CamTranslate( register Camera *cam, float tx, float ty, float tz )
- {
- Transform T;
-
- TmSpaceTranslate( T, tx, ty, tz, cam->space );
- TmConcat(T, cam->camtoworld, cam->camtoworld);
- cam->flag |= CAMF_NEWC2W;
- }
-
- /* CamScale is a noop if the camera is not in Euclidean space */
- void
- CamScale( register Camera *cam, float sx, float sy, float sz )
- {
- if (cam->space == TM_EUCLIDEAN) {
- CtmScale( cam->camtoworld, sx, sy, sz );
- cam->flag |= CAMF_NEWC2W;
- }
- }
-
- void
- CamAlignZ( register Camera *cam, float x, float y, float z )
- {
- Point axis;
-
- axis.x = x;
- axis.y = y;
- axis.z = z;
- CtmAlignZ( cam->camtoworld, &axis );
- cam->flag |= CAMF_NEWC2W;
- }
-
- /*
- * Apply T to camera as seen by world (== T^-1 to world, as seen by camera)
- */
- void
- CamTransform( register Camera *cam, Transform T )
- {
- TmConcat(T, cam->camtoworld, cam->camtoworld);
- cam->flag |= CAMF_NEWC2W;
- }
-
- static void
- CamStereoCompute( register Camera *cam )
- {
- float tanconv = tan(cam->stereo_angle);
- TmTranslate( cam->stereyes[CAM_RIGHT], cam->stereo_sep, 0., 0. );
- TmTranslate( cam->stereyes[CAM_LEFT], -cam->stereo_sep, 0., 0. );
- cam->stereyes[CAM_RIGHT][Z][X] = -tanconv;
- cam->stereyes[CAM_LEFT][Z][X] = tanconv;
- }
-
- /*-----------------------------------------------------------------------
- * Function: SetHalfField
- * Description: set camera's "halffield" value
- * Args: *cam: the camera
- * halffield: the halffied value to set to
- * Returns: nothing
- * Author: mbp
- * Date: Wed Aug 21 14:26:43 1991
- * Notes: This procedure modifies the halfyfield member of cam
- * in such a way as to guarantee that the min half-width
- * of the view window is halffield. This depends on the
- * camera's current aspect ratio.
- */
- static void
- SetHalfField( register Camera *cam, float halffield )
- {
- cam->halfyfield =
- (cam->frameaspect < 1 && cam->frameaspect > 0)
- ? halffield / cam->frameaspect
- : halffield;
- if(cam->flag & CAMF_PERSP)
- cam->halfyfield *= cam->focus;
- }
-
- /*-----------------------------------------------------------------------
- * Function: GetHalfField
- * Description: return camera's "halffield" value
- * Args: *cam: the camera
- * Returns: the halffield value
- * Author: mbp
- * Date: Wed Aug 21 14:29:31 1991
- * Notes: the "halffield" is the min half-width of the view
- * window. If the aspect ratio is >= 1, this is
- * the vertical half-width (halfyfield). If the aspect
- * ratio is < 1, this is the horizontal half-width.
- */
- static float
- GetHalfField( register Camera *cam )
- {
- float v = cam->halfyfield;
- if(cam->frameaspect < 1) v *= cam->frameaspect;
- if(cam->flag & CAMF_PERSP) v /= cam->focus;
- return v;
- }
-
- /*
- * Merge one Camera's changed values into another Camera
- */
- Camera *
- CamMerge(register Camera *src, register Camera *dst)
- {
- int chg;
- float fov;
-
- if(src == NULL) return dst;
- if(dst == NULL) return NULL;
-
- chg = src->changed;
-
- if(chg & CAMF_NEWC2W)
- CamSet(dst, CAM_C2WHANDLE, src->c2whandle, CAM_C2W, src->camtoworld, CAM_END);
- if(chg & CAMF_STEREOGEOM)
- CamSet(dst, CAM_STEREOSEP, src->stereo_sep,
- CAM_STEREOANGLE, src->stereo_angle, CAM_END);
- if(chg & CAMF_STEREOXFORM)
- CamSet(dst, CAM_STEREYES, src->stereyes,
- CAM_STERHANDLES, src->sterhandle, CAM_END);
- if(chg & CAMF_W2C)
- CamSet(dst, CAM_W2CHANDLE, src->w2chandle,
- CAM_W2C, src->worldtocam, CAM_END);
- CamGet(src, CAM_FOV, &fov);
- if(chg & CAMF_FOCUS) CamSet(dst, CAM_FOCUS, src->focus, CAM_END);
- if(chg & CAMF_PERSP) CamSet(dst,CAM_PERSPECTIVE,src->flag&CAMF_PERSP,CAM_END);
- if(chg & CAMF_FOV) CamSet(dst, CAM_FOV, fov, CAM_END);
- if(chg & CAMF_ASPECT) CamSet(dst, CAM_ASPECT, src->frameaspect, CAM_END);
- if(chg & CAMF_NEAR) dst->near = src->near;
- if(chg & CAMF_FAR) dst->far = src->far;
- if(chg & CAMF_EYE) dst->whicheye = src->whicheye;
- if(chg & CAMF_STEREO) CamSet(dst,CAM_STEREO,src->flag&CAMF_STEREO,CAM_END);
- if(chg & CAMF_SPACE) dst->space = src->space;
- return dst;
- }
-